package com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fastloan.common.model.CheckResult;
import com.fastloan.mgt.application.base.enums.ApplicationStatusEnum;
import com.fastloan.mgt.application.base.model.Application;
import com.fastloan.mgt.application.contract.enums.LoanModeEnum;
import com.fastloan.mgt.application.contract.model.ApplicationContract;
import com.fastloan.mgt.application.contract.model.ApplicationLoanResult;
import com.fastloan.mgt.application.contract.service.impl.loanmode.LoanMode;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.enmus.RyApplicationStatus;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.enmus.RyLoanStatusEnum;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.enmus.RyRepaymentStatus;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.model.RyLoanInfo;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.model.RyLoanResult;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.model.RyRepaymentPlan;
import com.fastloan.mgt.application.contract.service.impl.loanmode.mode.ry.model.RyRepaymentRequest;
import com.fastloan.mgt.application.enums.AppEnum;
import com.fastloan.mgt.application.repay.model.ZyApplicationRepaymentPlan;
import com.fastloan.mgt.application.repay.model.vo.AppCurrRepayExt;
import com.fastloan.mgt.application.repay.model.vo.RepayExt;
import com.fastloan.mgt.customer.model.ZyCustomer;
import com.fastloan.mgt.product.basics.model.Product;
import com.fastloan.mgt.verify.enums.ApiStore;
import com.fastloan.mgt.verify.model.ApiResult;
import com.fastloan.mgt.verify.util.ApiUtil;
import com.fastloan.utils.DateTimeUtil;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;

/**
 * 融钰放款模式
 *
 * @author yanwe
 * createTime 2018-01-2018/1/7 14:02
 */
@Component
public class RYLoanMode extends LoanMode {

    @Override
    public CheckResult loan(ApplicationContract applicationContract) {
        RyLoanInfo ryLoanInfo = new RyLoanInfo();
        ryLoanInfo.setAgent_order_no(applicationContract.getApplicationId());
        //转换放款金额从元到分
        ryLoanInfo.setLoan_amount(convertLoanAmount(applicationContract.getLoanAmount()));
        String format = getMoneyString(applicationContract.getLoanMonthRate());
        ryLoanInfo.setLoan_rate(format);
        ryLoanInfo.setLoan_rate_unit("月");
        ryLoanInfo.setLoan_period(applicationContract.getCreditPeriod());
        ryLoanInfo.setLoan_period_unit("月");
        ryLoanInfo.setRepayment_way(getProduct(applicationContract.getApplicationId()).getRepayment());
        ZyCustomer customer = getCustomer(applicationContract.getApplicationId());
        ryLoanInfo.setPayee_bank_account(customer.getCname());//TODO:收款人待定
        ryLoanInfo.setPayee_bank_name(applicationContract.getPayeeBankName());//TODO:收款人待定
        ryLoanInfo.setPayee_bank_card(applicationContract.getLoanAccount());//TODO:收款人待定
        ryLoanInfo.setBorrower_idno(customer.getCardNum());
        ryLoanInfo.setBorrower_name(customer.getCname());
        ryLoanInfo.setBorrower_idtype("1");//身份证
        ryLoanInfo.setBorrower_contact(applicationContract.getBorrowerContact());
        ryLoanInfo.setBorrower_bank_code(applicationContract.getBorrowerBankCode());
        ryLoanInfo.setBorrower_bank_card(applicationContract.getLoanAccount());
        ApiResult apiResult = ApiUtil.sendJsonSync(JSON.toJSONString(ryLoanInfo), ApiStore.RY_LOAN);
        logger.info("放款信息:"+ryLoanInfo);
        if (!ApiUtil.KX_SUCCESS_CODE.equals(apiResult.getRet())){
            String errorMsg = "放款请求失败！进件ID："+applicationContract.getApplicationId()+";"+apiResult;
            logger.warn(errorMsg);
            return CheckResult.fail(errorMsg);
        }
        RyLoanResult ryLoanResult = apiResult.getData(RyLoanResult.class);
        ApplicationLoanResult applicationLoanResult = new ApplicationLoanResult();
        applicationLoanResult.setApplicationId(applicationContract.getApplicationId());
        applicationLoanResult.setCreateTime(new Date());
        applicationLoanResult.setLoanStatus(RyLoanStatusEnum.ING.getStatus());
        applicationLoanResult.setLoanNo(ryLoanResult.getOrder_no());
        applicationLoanResultMapper.insert(applicationLoanResult);
        applicationService.modifyApplicationStatus(applicationContract.getApplicationId(), ApplicationStatusEnum.APP_LENDING);
        return CheckResult.success();
    }

    @Override
    @Transactional
    public CheckResult repayment(String applicationId) {
        Map<String, Object> map = new HashMap<>();
        map.put("appId", applicationId);
        List<RepayExt> repaymentPlans = applicationRepaymentPlanService.queryByAppId(map);
        if (repaymentPlans.isEmpty()){
            String errorMsg = "该进件没有还款计划无法还款:"+applicationId;
            logger.warn(errorMsg);
            return CheckResult.fail(errorMsg);
        }
        Optional<RepayExt> currentPlan = repaymentPlans.stream().filter(RepayExt::isCurrentPay).findFirst();
        if (!currentPlan.isPresent()){
            String errorMsg = "不存在未还款计划:"+applicationId;
            logger.warn(errorMsg);
            return CheckResult.fail(errorMsg);
        }
        Application application = applicationService.selectByPrimaryKey(applicationId);
        ApplicationLoanResult applicationLoanResult = applicationLoanResultMapper.selectByPrimaryKey(applicationId);
        Product product = productService.selectByPrimaryKey(application.getProductId());
        ApplicationContract criteria = new ApplicationContract();
        criteria.setApplicationId(applicationId);
        ApplicationContract applicationContract = applicationContractService.selectOne(criteria);
        ZyCustomer customer = customerService.selectByPrimaryKey(application.getCustomerId());
        RyRepaymentRequest ryRepaymentRequest = new RyRepaymentRequest();
        ryRepaymentRequest.setAgent_order_no(applicationId);
        ryRepaymentRequest.setType(1);//1 债权已存在，2 接入方新建
        ryRepaymentRequest.setOrder_no(applicationLoanResult.getLoanNo());
        ryRepaymentRequest.setTerm_no(currentPlan.get().getTermNo());
        ryRepaymentRequest.setRepayment_type(1);//1 正常还款，2 提前一次性还清，3 逾期还款
        ryRepaymentRequest.setRepayment_way(product.getRepayment());
        ryRepaymentRequest.setLoan_amount(convertLoanAmount(applicationContract.getLoanAmount()));
        ryRepaymentRequest.setBorrower_name(customer.getCname());
        ryRepaymentRequest.setBorrower_idtype("1");//身份证
        ryRepaymentRequest.setBorrower_idno(customer.getCardNum());
        ApiResult apiResult = ApiUtil.sendJsonSync(JSON.toJSONString(ryRepaymentRequest), ApiStore.RY_REPAYMENT);
        if (!apiResult.getRet().equals(ApiUtil.KX_SUCCESS_CODE)){
            return CheckResult.fail(apiResult.getMsg());
        }
        JSONObject jsonObject = JSON.parseObject(apiResult.getData());
        String status = jsonObject.getString("status");
        RyRepaymentStatus repaymentByStatus = RyRepaymentStatus.getRepaymentByStatus(status);
        if (repaymentByStatus.equals(RyRepaymentStatus.IS_REPAYMENT)
                || repaymentByStatus.equals(RyRepaymentStatus.CREATE_REPAYMENT)){
            updateRepaymentPlan(applicationId);//更新还款计划
            return CheckResult.success();
        }
        return CheckResult.fail(repaymentByStatus.getStatusName());
    }

    @Override
    @Transactional
    public CheckResult processCallback(ApplicationLoanResult applicationLoanResult) {
        ApplicationStatusEnum statusEnum = applicationStatusMap(applicationLoanResult.getLoanStatus());
        if (statusEnum == null){
            return CheckResult.fail("未知的融钰放款状态:"+applicationLoanResult.getLoanStatus());
        }
        CheckResult finalResult = CheckResult.success();
        if (statusEnum.equals(ApplicationStatusEnum.WAIT_REPAYMENT)){//放款成功
            CheckResult repayPlanCheck = selectRepayPlan(applicationLoanResult.getLoanNo());
            if (!repayPlanCheck.isCheckResult()){//放款成功但查询还款计划不存在
                logger.warn("放款回调成功但还款计划不存在,融钰订单:"+applicationLoanResult.getLoanNo()
                        + ",进件编号:"+applicationLoanResult.getApplicationId());
                return repayPlanCheck;
            } else {
                RyRepaymentPlan repaymentPlan = repayPlanCheck.getData(RyRepaymentPlan.class);
                List<RyRepaymentPlan.RepaymentPlan> repaymentList = repaymentPlan.getRepayment_list();
                CheckResult checkResult = insertPlan(applicationLoanResult, repaymentList);
                if (!checkResult.isCheckResult()){
                    return checkResult;
                }
            }
        } else {//放款失败
            String msg = "放款失败,进件ID:"+applicationLoanResult.getApplicationId()+",状态:"+statusEnum.getTypeName();
            logger.warn(msg);
            finalResult = CheckResult.fail(msg);
        }
        applicationService.modifyApplicationStatus(applicationLoanResult.getApplicationId(),statusEnum);
        applicationLoanResultMapper.updateByPrimaryKeySelective(applicationLoanResult);
        loanCallback.loanCallback(applicationLoanResult);
        return finalResult;
    }

    @Override
    public LoanModeEnum loanMode() {
        return LoanModeEnum.R_ONG_YU;
    }

    @Override
    @Transactional
    public CheckResult updateApplicationList(AppCurrRepayExt application) {
        CheckResult updatePlanResult = updateRepaymentPlan(application.getApplicationId());
        if (!updatePlanResult.isCheckResult()){
            return updatePlanResult;
        }
        RyRepaymentPlan repaymentPlan = updatePlanResult.getData(RyRepaymentPlan.class);
        ApplicationStatusEnum applicationStatusEnum = ryAppStatusMap(repaymentPlan.getStatus());
        if (applicationStatusEnum == null){
            return CheckResult.fail("未知的融钰订单状态:"+repaymentPlan.getStatus());
        }
        List<RyRepaymentPlan.RepaymentPlan> repaymentList = repaymentPlan.getRepayment_list();
        repaymentList.sort(Comparator.comparing(plan -> DateTimeUtil.format(plan.getRepayment_date()
                ,DateTimeUtil.NORM_DATE_PATTERN)));
        Optional<RyRepaymentPlan.RepaymentPlan> planOptional = repaymentList.stream().filter(plan ->
                !RyRepaymentStatus.IS_REPAYMENT
                        .equals(RyRepaymentStatus.getRepaymentByStatus(plan.getStatus()))).findFirst();
        if (!planOptional.isPresent()){
            return CheckResult.fail("还款计划不存在!");
        }
        RyRepaymentPlan.RepaymentPlan currentPlan = planOptional.get();
        application.setCurrentPay(convertMoneyFen2Yuan(currentPlan.getRepayment_amount()));
        application.setApplicationStatus(applicationStatusEnum.getType());
        application.setAppNeedPay(convertMoneyFen2Yuan(currentPlan.getRemain_interest())
                .add(convertMoneyFen2Yuan(currentPlan.getRemain_corpus()))
                .add(convertMoneyFen2Yuan(currentPlan.getRepayment_amount())));
        AppEnum.RepayStatus repaymentStatus = repaymentStatusMap(currentPlan.getStatus());
        if (repaymentStatus == null){
            return CheckResult.fail("未知的融钰还款状态:" + currentPlan.getStatus());
        }
        application.setRepaymentStatus(repaymentStatus.getCode());
        return CheckResult.success();
    }

    /**
     * 更新还款计划
     *
     * @param applicationId 进件id
     * @return 还款计划
     */
    public CheckResult updateRepaymentPlan(String applicationId){
        ApplicationLoanResult applicationLoanResult = applicationLoanResultMapper
                .selectByPrimaryKey(applicationId);
        CheckResult checkResult = selectRepayPlan(applicationLoanResult.getLoanNo());
        if (!checkResult.isCheckResult()){
            return checkResult;
        }
        RyRepaymentPlan repaymentPlan = checkResult.getData(RyRepaymentPlan.class);
        List<RyRepaymentPlan.RepaymentPlan> repaymentList = repaymentPlan.getRepayment_list();
        CheckResult insertResult = insertPlan(applicationLoanResult, repaymentList);
        if (!insertResult.isCheckResult()){
            return insertResult;
        }
        //更新进件状态
        ApplicationStatusEnum applicationStatusEnum = ryAppStatusMap(repaymentPlan.getStatus());
        applicationService.modifyApplicationStatus(applicationId,applicationStatusEnum);
        return CheckResult.success(repaymentPlan);
    }

    private CheckResult selectRepayPlan(String loanNo){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("order_no",loanNo);
        ApiResult apiResult = ApiUtil.sendJsonSync(jsonObject.toJSONString(), ApiStore.RY_REPAYMENT_PLAN);
        if (!apiResult.getRet().equals(ApiUtil.KX_SUCCESS_CODE)){
            return CheckResult.fail(apiResult.getMsg());
        }
        RyRepaymentPlan repaymentPlan = apiResult.getData(RyRepaymentPlan.class);
        return CheckResult.success(repaymentPlan);
    }

    private CheckResult insertPlan(ApplicationLoanResult applicationLoanResult
            , List<RyRepaymentPlan.RepaymentPlan> repaymentList) {
        List<ZyApplicationRepaymentPlan> applicationRepaymentPlanList = new ArrayList<>();
        for (RyRepaymentPlan.RepaymentPlan plan : repaymentList) {
            ZyApplicationRepaymentPlan applicationRepaymentPlan = new ZyApplicationRepaymentPlan();
            applicationRepaymentPlan.setAppId(applicationLoanResult.getApplicationId());
            applicationRepaymentPlan.setPlanPeriodInterest(convertMoneyFen2Yuan(plan.getRepayment_interest()));
            applicationRepaymentPlan.setRepaymentOverdueDate(DateTimeUtil.format(plan.getRepayment_date()
                    , DateTimeUtil.NORM_DATE_PATTERN));
            applicationRepaymentPlan.setCreateTime(new Date());
            AppEnum.RepayStatus repaymentStatus = repaymentStatusMap(plan.getStatus());
            if (repaymentStatus == null){
                return CheckResult.fail("未知的融钰还款状态:" + plan.getStatus());
            }
            applicationRepaymentPlan.setRepaymentStatus(repaymentStatus.getCode());
            applicationRepaymentPlan.setPlanPeriodPrincipal(convertMoneyFen2Yuan(plan.getRepayment_corpus()));
            applicationRepaymentPlan.setFactRepaymentAount(convertMoneyFen2Yuan(plan.getActual_amount()));
            applicationRepaymentPlan.setTermNo(plan.getTerm_no());
            BigDecimal actualAmount = new BigDecimal(plan.getActual_amount());
            BigDecimal repaymentAmount = new BigDecimal(plan.getRepayment_amount());
            applicationRepaymentPlan.setOffsetAmount(convertMoneyFen2Yuan(repaymentAmount.subtract(actualAmount).toString()));
            applicationRepaymentPlanList.add(applicationRepaymentPlan);
        }
        ZyApplicationRepaymentPlan criteria = new ZyApplicationRepaymentPlan();
        criteria.setAppId(applicationLoanResult.getApplicationId());
        applicationRepaymentPlanService.delete(criteria);
        synchronized (this){
            boolean allMatch = applicationRepaymentPlanList.stream()
                    .map(applicationPlan -> applicationRepaymentPlanService.insertSelective(applicationPlan))
                    .allMatch(isSuc -> isSuc == 1);
            if (!allMatch){
                logger.warn("还款计划生成失败,融钰订单:"+applicationLoanResult.getLoanNo()
                        + ",进件编号:"+applicationLoanResult.getApplicationId());
                return CheckResult.fail("还款计划生成失败！");
            }
        }
        return CheckResult.success();
    }

    private BigDecimal convertMoneyFen2Yuan(String money){
        return new BigDecimal(money)
                .divide(BigDecimal.valueOf(100),4,BigDecimal.ROUND_UP);
    }

    private int convertLoanAmount(BigDecimal loanAmount) {
        return loanAmount.multiply(BigDecimal.valueOf(100)).intValue();
    }

    private String getMoneyString(BigDecimal loanMonthRate) {
        DecimalFormat decimalFormat = new DecimalFormat("#.0000");
        return decimalFormat.format(loanMonthRate.setScale(4, BigDecimal.ROUND_UP));
    }

    /**
     * 融钰放款状态 -> 进件状态
     * 映射
     *
     * @param loanStatus 放款状态
     * @return 进件状态
     */
    private ApplicationStatusEnum applicationStatusMap(Integer loanStatus){
        switch (RyLoanStatusEnum.getByStatus(loanStatus)){
            case SUCCESS:
                return ApplicationStatusEnum.WAIT_REPAYMENT;
            case FAIL:
                return ApplicationStatusEnum.LOAN_FAIL;
            case REJECT:
                return ApplicationStatusEnum.LOAN_REJECT;
            default:
                return null;
        }
    }

    /**
     * 融钰还款状态 -> 快贷还款状态
     * 映射
     *
     * @param status 融钰还款状态
     * @return 快贷还款状态
     */
    private AppEnum.RepayStatus repaymentStatusMap(String status){
        switch (RyRepaymentStatus.getRepaymentByStatus(status)){
            case NO_REPAYMENT:
                return AppEnum.RepayStatus.NOPAY;
            case CREATE_REPAYMENT:
                return AppEnum.RepayStatus.NOPAY;
            case REPAYMENT_FAIL:
                return AppEnum.RepayStatus.NOPAY;
            case IS_REPAYMENT:
                return AppEnum.RepayStatus.ALREADY;
            case OVERDUE:
                return AppEnum.RepayStatus.OVERDUE;
            default:
                return null;
        }

    }

    /**
     * 融钰订单状态 -> 快贷进件状态
     * 映射
     *
     * @param status 融钰订单状态
     * @return 进件状态
     */
    private ApplicationStatusEnum ryAppStatusMap(String status){
        switch (RyApplicationStatus.getByStatus(status)){
            case WAIT_AUDIT:
                return ApplicationStatusEnum.APP_LENDING;
            case WAIT_LOAN:
                return ApplicationStatusEnum.APP_LENDING;
            case LOAN_ING:
                return ApplicationStatusEnum.APP_LENDING;
            case AUDIT_REJECT:
                return ApplicationStatusEnum.LOAN_REJECT;
            case WAIT_REPAYMENT:
                return ApplicationStatusEnum.WAIT_REPAYMENT;
            case LOAN_FAIL:
                return ApplicationStatusEnum.LOAN_FAIL;
            case ADVANCE_REPAYMENT_FINISH:
                return ApplicationStatusEnum.FINISH;
            case FINISH:
                return ApplicationStatusEnum.FINISH;
            case OVERDUE:
                return ApplicationStatusEnum.OVERDUE;
            default:
                return null;
        }
    }
}
